// Implements dm_plugin_load and base DMPlugin methods

//Please refer to http://dansguardian.org/?page=copyright2
//for the license for this code.

//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


// INCLUDES
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#ifdef HAVE_CONFIG_H
#include "../mind_config.h"
#endif
#include "DownloadManager.hpp"
#include "ConfigVar.hpp"
#include "OptionContainer.hpp"
#include "RegExp.hpp"
#include "Logger.hpp"

// GLOBALS
extern OptionContainer o;
extern Logger log;
extern dmcreate_t defaultdmcreate;

// find the class factory functions for any DM plugins we've been configured to build

#ifdef ENABLE_FANCYDM
extern dmcreate_t fancydmcreate;
#endif

#ifdef ENABLE_TRICKLEDM
extern dmcreate_t trickledmcreate;
#endif


// IMPLEMENTATION

//
// DMPlugin
//

// constructor

DMPlugin::DMPlugin(ConfigVar &definition) : alwaysmatchua(false), cv(definition) {
}

// default initialisation procedure

int DMPlugin::init(void* args) {
    bool lastplugin = *((bool*)args);
    if (!lastplugin) {
        // compile regex for matching supported user agents
        String r(cv["useragentregexp"]);
        if (r.length() > 0) {
#ifdef MIND_DEBUG
            std::cout << "useragent regexp: " << r << std::endl;
#endif
            ua_match.comp(r.toCharArray());
        } else {
            // no useragent regex? then default to .*
#ifdef MIND_DEBUG
            std::cout << "no useragent regular expression; defaulting to .*" << std::endl;
#endif
            alwaysmatchua = true;
        }
        if (!readStandardLists())
            return -1;
    }
#ifdef MIND_DEBUG
    else
        std::cout << "Fallback DM plugin; no matching options loaded" << std::endl;
#endif
    return 0;
}

// default method for sending the client a download link

void DMPlugin::sendLink(Socket &peersock, String &linkurl, String &prettyurl) {
    // 1220 "<p>Scan complete.</p><p>Click here to download: "
    String message(o.language_list.getTranslation(1220));
    message += "<a href=\"" + linkurl + "\">" + prettyurl + "</a></p></body></html>\n";
    peersock.writeString(message.toCharArray());
}

// default method for deciding whether we will handle a request

bool DMPlugin::willHandle(HTTPHeader *requestheader, HTTPHeader *docheader) {
    // match user agent first (quick)
    if (!(alwaysmatchua || ua_match.match(requestheader->userAgent().toCharArray())))
        return false;

    // then check standard lists (mimetypes & extensions)

    // mimetypes
    String mimetype("");
    bool matchedmime = false;
    if (mimelistenabled) {
        mimetype = docheader->getContentType();
#ifdef MIND_DEBUG
        std::cout << "mimetype: " << mimetype << std::endl;
#endif
        if (mimetypelist.findInList(mimetype.toCharArray()) == NULL) {
            if (!extensionlistenabled)
                return false;
        } else
            matchedmime = true;
    }

    if (extensionlistenabled && !matchedmime) {
        // determine the extension
        String path(requestheader->decode(requestheader->url()));
        path.removeWhiteSpace();
        path.toLower();
        path.removePTP();
        path = path.after("/");
        path.hexDecode();
        path.realPath();
        String disposition(docheader->disposition());
        String extension;
        if (disposition.length() > 2) {
            extension = disposition;
            while (extension.contains(".")) {
                extension = extension.after(".");
            }
            extension = "." + extension;
        } else {
            if (!path.contains("?")) {
                extension = path;
            } else {
                if (mimetype.length() == 0)
                    mimetype = docheader->getContentType();
                if (mimetype.contains("application/")) {
                    extension = path;
                    if (extension.contains("?")) {
                        extension = extension.before("?");
                    }
                }
            }
        }
#ifdef MIND_DEBUG
        std::cout << "extension: " << extension << std::endl;
#endif
        // check the extension list
        if (!extension.contains(".") || (extensionlist.findEndsWith(extension.toCharArray()) == NULL))
            return matchedmime;
    }

    return true;
}

// read in all the lists of various things we wish to handle

bool DMPlugin::readStandardLists() {
    mimetypelist.reset(); // incase this is a reload
    extensionlist.reset();

    String filename(cv["managedmimetypelist"]);
    if (filename.length() > 0) {
        if (!mimetypelist.readItemList(filename.toCharArray(), false, 0)) {
            log.writeToLog(1, "Error opening managedmimetypelist");
            return false;
        }
        mimetypelist.doSort(false);
        mimelistenabled = true;
    } else {
        mimelistenabled = false;
    }

    filename = cv["managedextensionlist"];
    if (filename.length() > 0) {
        if (!extensionlist.readItemList(filename.toCharArray(), false, 0)) {
            log.writeToLog(1, "Error opening managedextensionlist");
            return false;
        }
        extensionlist.doSort(false);
        extensionlistenabled = true;
    } else {
        extensionlistenabled = false;
    }

    return true;
}

// take in a DM plugin configuration file, find the DMPlugin descendent matching the value of plugname, and store its class factory funcs for later use

DMPlugin* dm_plugin_load(const char *pluginConfigPath) {
    ConfigVar cv;

    if (cv.readVar(pluginConfigPath, "=") > 0) {
        log.writeToLog(1, "Unable to load plugin config %s", pluginConfigPath);
        return NULL;
    }

    String plugname(cv["plugname"]);

    if (plugname.length() < 1) {
        log.writeToLog(1, "Unable read plugin config plugname variable %s", pluginConfigPath);
        return NULL;
    }

    if (plugname == "default") {
#ifdef MIND_DEBUG
        std::cout << "Enabling default DM plugin" << std::endl;
#endif
        return defaultdmcreate(cv);
    }

#ifdef ENABLE_FANCYDM
    if (plugname == "fancy") {
#ifdef MIND_DEBUG
        std::cout << "Enabling fancy DM plugin" << std::endl;
#endif
        return fancydmcreate(cv);
    }
#endif

#ifdef ENABLE_TRICKLEDM
    if (plugname == "trickle") {
#ifdef MIND_DEBUG
        std::cout << "Enabling trickle DM plugin" << std::endl;
#endif
        return trickledmcreate(cv);
    }
#endif


    log.writeToLog(1, "Unable to load plugin %s", plugname.toCharArray());
    return NULL;
}
